3 函数定义 算术平均收益率计算
3.1 引言函数在金融编程中的重要性
函数是程序设计中的基本抽象单元,它将一系列操作封装起来,使代码更加模块化、可重用和易维护。在金融计算中,函数尤其重要,因为许多金融指标和模型(如收益率、波动率、VaR等)需要反复计算。
理论背景:函数式编程思想
从编程范式来看,函数体现了几种核心思想: 1. 抽象(Abstraction): 隐藏实现细节,只暴露接口 2. 分解(Decomposition): 将复杂问题分解为简单子问题 3. 复用(Reuse): 一次编写,多次调用
Python支持多种编程范式,包括函数式编程。理解函数的数学定义(输入到输出的映射)对编写高质量金融代码至关重要。
3.2 算术平均收益率
算术平均(Arithmetic Mean)是最常用的集中趋势度量,在金融中广泛用于计算平均收益率。
3.2.1 数学定义与性质
对于收益率序列 \(r_1, r_2, ..., r_n\), 算术平均定义为:
\[ \bar{r} = \frac{1}{n} \sum_{i=1}^{n} r_i = \frac{r_1 + r_2 + \cdots + r_n}{n} \]
数学性质: 1. 线性性: \(\overline{aX + bY} = a\bar{X} + b\bar{Y}\) 2. 最小二乘性: 算术平均使误差平方和最小 3. 敏感性: 对极端值敏感,受异常值影响大
金融含义: 算术平均收益率适用于: - 单期期望收益率的估计 - 投资组合绩效评估 - 风险调整后收益率的基准
易混淆概念辨析:算术平均 vs. 几何平均
| 特性 | 算术平均 | 几何平均 |
|---|---|---|
| 公式 | \(\bar{r}_A = \frac{1}{n}\sum r_i\) | \(\bar{r}_G = \left[\prod(1+r_i)\right]^{1/n} - 1\) |
| 应用场景 | 单期期望收益 | 多期复利增长 |
| 数值关系 | \(\bar{r}_A \geq \bar{r}_G\) | 波动率越大,差距越大 |
| 金融含义 | 期初财富的期望增长率 | 期末财富的实际增长率 |
关键理解: 如果你想知道”平均来说每期赚多少”,用算术平均;如果你想计算”长期复利增长”,用几何平均。
3.2.2 Python函数定义基础
Python提供多种定义函数的方式:
# ==================== 方式1: 标准def定义 ====================
# def关键字用于定义函数,是Python中最常用的函数定义方式
# arithmetic_mean是函数名,遵循标识符命名规则(字母、数字、下划线,不能以数字开头)
# numbers是参数名,括号内是函数的参数列表
def arithmetic_mean(numbers): # 定义函数名为arithmetic_mean,接收一个参数numbers
"""计算算术平均数""" # 文档字符串(Docstring),用三引号包围,描述函数功能
return sum(numbers) / len(numbers) # return返回计算结果:sum求和,len求长度,/计算除法
# ==================== 方式2: lambda表达式(匿名函数) ====================
# lambda用于定义匿名函数,适合简单的单行函数
# 语法格式: lambda parameters: expression
# x是参数,sum(x) / len(x)是返回表达式
arithmetic_mean_lambda = lambda x: sum(x) / len(x) # 定义lambda函数并赋值给变量
# ==================== 测试两种定义 ====================
# 创建测试数据列表,包含5个数值
data = [1, 2, 3, 4, 5] # 方括号创建列表,元素用逗号分隔
print(arithmetic_mean(data)) # 调用def定义的函数,print输出结果: 3.0
print(arithmetic_mean_lambda(data)) # 调用lambda函数,输出: 3.0代码深度解析:
def语句的解剖:
def function_name(parameters): """文档字符串(Docstring)""" function_body return valuedef: 函数定义关键字function_name: 函数名,遵循标识符命名规则parameters: 参数列表,多个参数用逗号分隔return: 返回值,无return则返回None
lambda表达式的限制:
- 只能包含单个表达式
- 不能包含语句(如if, for, while)
- 自动返回表达式的值
- 适合简单函数,提高代码简洁性
Python中的函数是一等对象:
- 可以赋值给变量
- 可以作为参数传递
- 可以作为返回值
- 可以存储在数据结构中
3.2.3 任务实现计算股票的算术平均收益率
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
A_mean=lambda x:sum(x)/len(x) # 定义匿名函数A_mean
sh000001=[26.468,-10.7081,2.8477,43.5348,13.4337] # 定义列表sh000001
print(round(A_mean(sh000001),2)) # 输出四舍五入后的计算结果代码逐步解析:
lambda函数的拆解:
lambda x: sum(x) / len(x) # ^ ^^^^^^^^^^^^ # | | # 参数 返回表达式- 参数
x期望是一个可迭代对象(如列表) sum(x)计算列表元素总和len(x)获取列表长度/执行浮点除法(Python 3特性)
- 参数
数据处理流程:
[26.468, -10.7081, 2.8477, 43.5348, 13.4337] ↓ sum = 75.5762 len = 5 ↓ mean = 75.5762 / 5 = 15.11524 ↓ round(15.11524, 2) = 15.11round函数的行为:
round(number, ndigits): 四舍五入到指定小数位ndigits=2: 保留2位小数- 注意: Python 3采用”银行家舍入”(Round Half to Even)
金融实例分析:
上证指数在样本期内的平均年收益率为15.11%,这意味着: - 期望收益: 基于历史数据,投资者可期望每年获得约15.11%的回报 - 风险评估: 需结合波动率(标准差)评估风险调整后收益 - 基准比较: 可与无风险利率、其他市场指数进行比较
3.2.4 函数的进阶应用
在实际金融分析中,我们通常需要定义更复杂的函数:
# ==================== 定义完整函数 ====================
# def关键字定义标准函数
# calculate_mean_return是函数名,采用snake_case命名规范
# returns是必选参数:收益率列表
# decimals=2是可选参数:保留小数位数,默认值为2
def calculate_mean_return(returns, decimals=2): # 函数定义,包含两个参数
"""
计算算术平均收益率
参数:
returns (list): 收益率序列
decimals (int): 保留小数位数,默认为2
返回:
float: 算术平均收益率
异常:
ValueError: 如果输入为空列表或包含非数值
"""
# ==================== 参数验证 ====================
# if语句进行条件判断
# not returns检查列表是否为空
# raise用于抛出异常,中断程序执行
if not returns: # 如果returns列表为空
raise ValueError("收益率列表不能为空") # 抛出ValueError异常并提示
# all()函数检查所有元素是否满足条件
# isinstance(r, (int, float))检查r是否为整数或浮点数
# for r in returns遍历returns列表中的每个元素
# 这行代码确保所有收益率都是数值类型
if not all(isinstance(r, (int, float)) for r in returns): # 如果有非数值元素
raise ValueError("收益率必须为数值类型") # 抛出异常提示数据类型错误
# ==================== 计算平均值 ====================
# sum(returns)计算收益率总和
# len(returns)获取收益率个数
# / 执行除法,得到算术平均值
mean = sum(returns) / len(returns) # 计算算术平均收益率
# ==================== 格式化返回 ====================
# round函数四舍五入到指定小数位
# mean是待舍入的数值
# decimals是保留的小数位数
# return将结果返回给调用者
return round(mean, decimals) # 返回四舍五入后的平均值
# ==================== 使用示例 ====================
# try-except用于捕获和处理异常
# try块包含可能抛出异常的代码
try: # 尝试执行以下代码
# 调用calculate_mean_return函数
# sh000001是收益率数据
# decimals=3指定保留3位小数
result = calculate_mean_return(sh000001, decimals=3) # 调用函数并接收返回值
# f-string格式化输出
# {result:.3f}表示格式化为浮点数,保留3位小数
print(f"上证指数平均收益率: {result:.3f}%") # 输出格式化的结果
# except块处理特定类型的异常
except ValueError as e: # 捕获ValueError异常并赋值给e
# e是异常对象,包含错误信息
print(f"计算错误: {e}") # 输出错误信息代码质量要素:
- 文档字符串(Docstring):
- 描述函数功能
- 说明参数类型和含义
- 说明返回值
- 列出可能抛出的异常
- 参数验证:
- 检查输入有效性
- 提供清晰的错误信息
- 防止程序崩溃
- 默认参数:
- 提供合理默认值
- 增强函数灵活性
- 提高使用便利性
3.2.5 lambda表达式的高级用法
lambda表达式在金融数据处理中非常实用,特别是在结合map, filter, reduce等函数时:
# ==================== 导入模块 ====================
# from...import语句从模块中导入特定函数
# functools是Python标准库,提供高阶函数工具
# reduce函数用于累积计算
from functools import reduce # 导入reduce函数
# ==================== 创建示例数据 ====================
# portfolio_returns是一个字典
# 键(Key): 股票名称(Stock A, Stock B, Stock C)
# 值(Value): 收益率列表,每个列表包含5个交易日的收益率
portfolio_returns = { # 花括号创建字典
'Stock A': [0.05, 0.03, -0.02, 0.04, 0.06], # 股票A的收益率
'Stock B': [0.08, 0.02, 0.01, -0.03, 0.05], # 股票B的收益率
'Stock C': [-0.01, 0.04, 0.07, 0.02, 0.03] # 股票C的收益率
}
# ==================== 使用map应用函数 ====================
# map(function, iterable)将函数应用于可迭代对象的每个元素
# lambda returns: sum(returns) / len(returns)定义匿名函数计算平均收益率
# portfolio_returns.values()获取字典的所有值(收益率列表)
# map返回迭代器,需要用list()转换为列表
mean_calculator = lambda returns: sum(returns) / len(returns) # 定义计算均值的lambda函数
mean_returns = list(map(mean_calculator, portfolio_returns.values())) # 对每只股票计算平均收益
# f-string格式化输出
# mean_returns是一个列表,包含3只股票的平均收益率
print(f"各股票平均收益率: {mean_returns}") # 输出所有股票的平均收益率
# ==================== 使用filter筛选 ====================
# filter(function, iterable)根据函数条件筛选元素
# lambda r: r > 0定义条件:收益率大于0
# 中括号创建列表,包含多个收益率数据
# list()将filter对象转换为列表
positive_returns = lambda r: r > 0 # 定义判断是否为正收益的lambda函数
filtered_stocks = list(filter(positive_returns, [0.05, -0.02, 0.03, -0.01])) # 筛选正收益
print(f"正收益股票: {filtered_stocks}") # 输出筛选结果
# ==================== 使用reduce累积计算 ====================
# reduce(function, sequence, initial)对序列进行累积计算
# lambda x, y: x * (1 + y)定义累积函数:计算复利
# returns是收益率列表
# 1是初始值(代表初始本金1)
# -1在最后减去1,得到净收益率
returns = [0.05, 0.03, -0.02, 0.04] # 创建收益率列表
cumulative_return = reduce(lambda x, y: x * (1 + y), returns, 1) - 1 # 计算累积收益率
# f-string格式化,.4f表示保留4位小数
print(f"累积收益率: {cumulative_return:.4f}") # 输出累积收益率3.3 算术平均的局限性与改进
3.3.1 局限性分析
算术平均虽然简单直观,但在金融应用中存在重要局限:
极端值敏感: 一个极端值会显著影响平均值
# 示例:极端值的影响 normal_returns = [0.05, 0.03, 0.04, 0.06, 0.02] with_outlier = normal_returns + [0.50] # 添加极端收益率 print(f"正常平均: {sum(normal_returns)/len(normal_returns):.4f}") print(f"含极端值: {sum(with_outlier)/len(with_outlier):.4f}")不适用于多期增长: 算术平均会高估长期复利增长
分布假设: 隐含假设收益率对称分布,但实际金融收益常呈偏态分布(Skewed Distribution)
3.3.2 改进方案稳健统计量
# ==================== 导入必要的库 ====================
# numpy是Python科学计算基础库,提供高性能数组运算
# as np给numpy起别名,简化代码书写
import numpy as np # 导入numpy库并简写为np
# ==================== 创建示例数据 ====================
# returns列表包含收益率数据,其中包含极端值0.50和-0.10
# 这种数据在金融中很常见,突发事件会造成极端收益
returns = [0.05, 0.03, 0.04, 0.50, 0.02, -0.10, 0.06] # 包含极端值的收益率数据
# ==================== 1. 算术平均 ====================
# np.mean()计算算术平均值
# arithmetic_mean对极端值敏感,会被0.50拉高
arithmetic_mean = np.mean(returns) # 计算算术平均值
# ==================== 2. 中位数 ====================
# np.median()计算中位数
# 中位数对极端值不敏感,是稳健的统计量
# 中位数是将数据排序后位于中间位置的值
median = np.median(returns) # 计算中位数
# ==================== 3. 截尾平均 ====================
# scipy是科学计算库,stats模块提供统计函数
from scipy import stats # 从scipy导入stats模块
# stats.trim_mean(data, proportion)计算截尾平均
# 0.1表示从两端各截去10%的数据
# 这样可以消除极端值的影响
trimmed_mean = stats.trim_mean(returns, 0.1) # 去掉两端各10%后计算平均
# ==================== 4. 缩尾平均 ====================
# stats.mstats.winsorize将极端值替换为分位数值
# limits=[0.1, 0.1]表示将最低10%和最高10%的值替换
# 这比直接删除数据更保守
winsorized_mean = stats.mstats.winsorize(np.array(returns), limits=[0.1, 0.1]).mean() # 转为数组后缩尾计算平均
# ==================== 输出比较结果 ====================
# f-string格式化输出,.4f保留4位小数
# 可以看到不同统计量的差异
print(f"算术平均: {arithmetic_mean:.4f}") # 输出算术平均
print(f"中位数: {median:.4f}") # 输出中位数
print(f"截尾平均: {trimmed_mean:.4f}") # 输出截尾平均
print(f"缩尾平均: {winsorized_mean:.4f}") # 输出缩尾平均选择建议: - 正常分布数据: 使用算术平均 - 含极端值: 使用中位数或截尾平均 - 投资组合基准: 使用算术平均(与CAPM一致) - 长期绩效评估: 使用几何平均
实际应用指南:
在实际金融分析中,选择合适的集中趋势度量至关重要。对于简单一次性函数,使用lambda可以提高代码简洁性;对于复杂逻辑,应使用def定义并添加完整文档字符串;同时始终验证输入参数的有效性,确保函数的健壮性。理解算术平均与几何平均的区别——前者用于单期期望收益估计,后者用于多期复利增长计算——是正确应用这些统计量的基础。
下一步学习: 在下一章中,我们将学习Python的内置函数,这些函数提供了丰富的基础功能,可以大大提高编程效率。